/*
 * Copyright 2002-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.spice.servlet.dispatcher.handler;


import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spice.servlet.dispatcher.XSDElements;
import org.spice.servlet.dispatcher.XSDHolder;
import org.spice.web.components.View;
import org.spice.web.components.interceptors.After;
import org.spice.web.components.interceptors.Before;
import org.spice.web.request.annotation.Get;
import org.spice.web.request.annotation.Post;
import org.spice.web.request.annotation.URLPattern;


/**
 * @author Karthik Rajkumar
 * @version (included from the RED-CHILLIES version)
 */
public
class RequestListeners {


	private static 
	final Logger LOGGER = LoggerFactory.getLogger(RequestListeners.class);
	
	private 
	static final String URL = "URL:";

	public 
	static void listenToRequest(final HttpServletRequest request, final HttpServletResponse response, ServletConfig servletConfig) {
		

		if (request.getMethod().equalsIgnoreCase("POST")) {

			LOGGER.info("THIS IS THE ENTRY for post");
			postHandler(request, response);
		} else {
			LOGGER.info("THIS IS THE ENTRY for GET");
			
			final String requestForward = request.getRequestURI().substring(
					request.getContextPath().length());
			LOGGER.info("The request forward is " + requestForward);
			SpiceSingleton bean = SpiceSingleton.getInstance();
			final List<Class<?>> controllers = bean.getListOfClasses();
			for (final Class<?> clazz : controllers) {
				if (clazz.isAnnotationPresent(View.class)) {
					Method[] methods = clazz.getDeclaredMethods();
					for (Method method : methods) {
						if (method.isAnnotationPresent(URLPattern.class)
								&& method.isAnnotationPresent(Get.class))
							methodHandler(request, response, requestForward,
									bean, clazz, method, servletConfig);
					}
				}
			}
		}
	}

	protected 
	static void postHandler(final HttpServletRequest request,
			final HttpServletResponse response) {
		
		SpiceSingleton beans = SpiceSingleton.getInstance();
		final List<Class<?>> controller = beans.getListOfClasses();
		for(final Class<?> clazz : controller){
			if(clazz.isAnnotationPresent(View.class)){
				Method[] methods = clazz.getDeclaredMethods();
				for(Method method : methods){
					LOGGER.info("The method is " + method.getName());
					if(method.isAnnotationPresent(URLPattern.class) && method.isAnnotationPresent(Post.class)){

						LOGGER.info("The method matched " + method.getName() + " and the annotations are present");
						URLPattern urlPattern = method.getAnnotation(URLPattern.class);
						final List<Object> listOfArguments = listOfArguments(request, response, method, beans);
						LOGGER.info("The URL Pattern values are " + urlPattern.values() + " and the request URI " + request.getRequestURI().substring(request.getContextPath().length()));
						if(urlPattern.values().equals(request.getRequestURI().substring(request.getContextPath().length()))){
							try {
								Object returnURL = method.invoke(beans.getClassInstance(clazz.getName()), listOfArguments.toArray());
								LOGGER.info("The return URL is " + returnURL);
								if(returnURL.toString().startsWith(URL))
									returnURL = returnURL.toString().substring(URL.length());
								response.sendRedirect(request.getContextPath() + returnURL.toString());
								return;
							} catch (IllegalArgumentException e) {
								e.printStackTrace();
							} catch (IllegalAccessException e) {
								e.printStackTrace();
							} catch (InvocationTargetException e) {
								e.printStackTrace();
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					
					}
				}
			}
		}
	}

	

	private 
	static void methodHandler(HttpServletRequest request, HttpServletResponse response, String requestForward, SpiceSingleton bean, Class<?> clazz, Method method, ServletConfig servletConfig) {


		final List<Object> listOfArguments = listOfArguments(request, response, method, bean);
		URLPattern urlPattern = method.getAnnotation(URLPattern.class);
		if(urlPattern.values().equals(requestForward)) {

			try {
				// If the interceptor is available for the method then the interceptor will be executed @see Before
				beforeIntercept(request, response, bean, requestForward);
				// The method will be invoked from the controllers.
				final Object returnURL = method.invoke(bean.getClassInstance(clazz.getName()), listOfArguments.toArray());
				// If the interceptor is available for the method then the interceptor will be executed @see After
				afterIntercept(request, response, bean, requestForward);
				parseResponse(response);

				final XSDHolder xsdHolder = XSDHolder.getInstance();
				final XSDElements xsdElements = xsdHolder.getXsdElements();
				final RequestDispatcher spiceDispatcher = request.getRequestDispatcher(xsdElements.getJspPrefix().concat(returnURL.toString()).concat(xsdElements.getJspSuffix()));
				spiceDispatcher.forward(request, response);
				return;
			}
			catch (IllegalAccessException e) {
				//To be handled with the SPICE Exception handling framework
				e.printStackTrace();
			}
			catch (InvocationTargetException e) {
				//To be handled with the SPICE Exception handling framework
				e.printStackTrace();
			}
			catch (Exception e){
				//To be handled with the SPICE Exception handling framework
				e.printStackTrace();
			}
		}
	}

	private
	static void afterIntercept(final HttpServletRequest request, final HttpServletResponse response, final SpiceSingleton bean, final String requestForward) {

		final List<Class<?>> beforeInterceptor = bean.getIntercept();
		for(final Class<?> before : beforeInterceptor){
			final Method[] methods = before.getDeclaredMethods();
			for(Method method : methods){
				if(method.isAnnotationPresent(After.class)){
					After afterIntercept = method.getAnnotation(After.class);
					for(final String url : afterIntercept.url()){
						if(url.equals(requestForward)){
							try {
								final List<Object> listOfArguments = listOfArguments(request, response, method, bean);
								final Object clazzInstance = before.newInstance();
								method.invoke(clazzInstance, listOfArguments.toArray() );

							}
							catch (InstantiationException e) {
								//To be added with the SPICE Exception
							}
							catch (IllegalAccessException e) {
								// To be added with the SPICE Exception
							}
							catch (InvocationTargetException e) {
								// To be added with the SPICE Exception
							}

						}
					}
				}
			}
		}



	}

	private 
	static List<Object> listOfArguments(HttpServletRequest request, HttpServletResponse response, Method method, SpiceSingleton bean) {
		Class<?>[] paramTypes = method.getParameterTypes();
		LOGGER.info("The param types size is " + paramTypes.length);
		final List<Object> listOfArguments = new ArrayList<Object>(paramTypes.length);
		for(Class<?> param : paramTypes){
			LOGGER.info("The param class names are " + param.getName());
			if(param.getName().equals("javax.servlet.http.HttpServletRequest")){
				listOfArguments.add(request);
			}else if(param.getName().equals("javax.servlet.http.HttpServletResponse")){
				listOfArguments.add(response);
			}else {
				try {
					Object newValueObject = param.newInstance();
					Field[] valueObjectMethods = param.getDeclaredFields();
					final Map<?, ?> objectArray = request.getParameterMap();
					if (objectArray != null)
						for (Map.Entry<?, ?> object : objectArray.entrySet()) {
							LOGGER.info("The key is " + object.getKey());
							if (object.getKey().toString().contains(",")) {
								LOGGER.info("The methods size is "
										+ valueObjectMethods.length);
								for (Field singleMethod : valueObjectMethods) {
									singleMethod.setAccessible(true);
									LOGGER.info("The methods in the value Objects are "
											+ singleMethod.getName());

									String[] splitter = object.getKey()
											.toString().split(",");
									String fieldName = splitter[1];
									if (singleMethod.getName()
											.equalsIgnoreCase(fieldName.trim())) {
										String[] array = (String[]) object
												.getValue();
										for (String singleArray : array) {
											singleMethod.set(newValueObject,
													singleArray);

										}
									}
								}
							}
						}
					
					listOfArguments.add(newValueObject);
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
			
			
			
			
		}
		LOGGER.info("The total number of the arguments inserted is " + listOfArguments.size());
		return listOfArguments;
	}

	/**
	 *
	 * @param request
	 * @param response
	 * @param bean
	 */
	private 
	static void beforeIntercept(final HttpServletRequest request, final HttpServletResponse response, final SpiceSingleton bean, final String requestForward) {
		final List<Class<?>> beforeInterceptor = bean.getIntercept();

		for(final Class<?> before : beforeInterceptor){
			final Method[] methods = before.getDeclaredMethods();
			for(Method method : methods){
				if(method.isAnnotationPresent(Before.class)){
					Before beforeIntercept = method.getAnnotation(Before.class);
					for(final String url : beforeIntercept.url()){
						if(url.equals(requestForward)){
							try {
								final List<Object> listOfArguments = listOfArguments(request, response, method, bean);
								final Object clazzInstance = before.newInstance();
								method.invoke(clazzInstance, listOfArguments.toArray() );

							}
							catch (InstantiationException e) {
								//To be added with the SPICE Exception
							}
							catch (IllegalAccessException e) {
								// To be added with the SPICE Exception
							}
							catch (InvocationTargetException e) {
								// To be added with the SPICE Exception
							}

						}
					}
				}
			}
		}


	}


	private static void parseResponse(HttpServletResponse response) {
		response.setHeader("Pragma", "private");
		response.setHeader("Cache-Control", "private, must-revalidate");
	}
}
